from abc import ABC, abstractmethod
from typing import List, Optional
from datetime import datetime
from models.lottery import LotteryDraw
from models.config import CRAWLER_CONFIG
import requests
import logging
import time
import random
from models.lottery import LotteryManager
from models.config import LOTTERY_TYPES

class BaseCrawler(ABC):
    """基础爬虫类"""
    def __init__(self, lottery_type: str):
        self.lottery_type = lottery_type
        self.config = CRAWLER_CONFIG
        self.logger = logging.getLogger(f"crawler_{lottery_type}")
        self.session = requests.Session()
        self.session.headers.update(self.config['headers'])

    def fetch_with_retry(self, url: str, max_retries: int = None, delay: int = None) -> Optional[requests.Response]:
        """带重试机制的请求方法"""
        max_retries = max_retries or self.config['max_retries']
        delay = delay or self.config['retry_delay']
        
        for attempt in range(max_retries):
            try:
                response = self.session.get(url, timeout=self.config['timeout'])
                response.raise_for_status()
                return response
            except requests.RequestException as e:
                self.logger.warning(f"请求失败 (尝试 {attempt + 1}/{max_retries}): {str(e)}")
                if attempt < max_retries - 1:
                    sleep_time = delay + random.uniform(0, 2)  # 添加随机延迟
                    self.logger.info(f"等待 {sleep_time:.2f} 秒后重试...")
                    time.sleep(sleep_time)
                else:
                    self.logger.error(f"达到最大重试次数，请求失败: {str(e)}")
                    return None

    @abstractmethod
    def fetch_latest_draw(self) -> Optional[LotteryDraw]:
        """获取最新一期开奖数据"""
        pass

    @abstractmethod
    def fetch_history_data(self) -> List[LotteryDraw]:
        """获取历史开奖数据"""
        pass

    def save_draws(self, draws: List[LotteryDraw]) -> None:
        """保存开奖数据"""
        manager = LotteryManager(self.lottery_type)
        for draw in draws:
            manager.save_draw(draw)
            self.logger.info(f"保存开奖数据: {draw.draw_number}")

    def is_draw_day(self) -> bool:
        """判断今天是否是开奖日"""
        today = datetime.now().weekday()
        return today in LOTTERY_TYPES[self.lottery_type]['draw_days']

    def is_after_draw_time(self) -> bool:
        """判断是否超过开奖时间"""
        draw_time = LOTTERY_TYPES[self.lottery_type]['draw_time']
        current_time = datetime.now().time()
        return current_time >= draw_time 